import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn import svm
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report, accuracy_score
from sklearn.datasets import load_iris

# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

# 加载鸢尾花数据集
iris = load_iris()
X = iris.data
y = iris.target
target_names = iris.target_names
feature_names = iris.feature_names

# 将数据集转换为DataFrame便于分析
iris_df = pd.DataFrame(X, columns=feature_names)
iris_df['target'] = y
iris_df['species'] = iris_df['target'].map({0: target_names[0], 1: target_names[1], 2: target_names[2]})

print("鸢尾花数据集描述:")
print(iris_df.describe())
print("\n类别分布:")
print(iris_df['species'].value_counts())


# 实验函数
def run_svm_experiment(test_size, kernel='rbf', C=1.0, gamma='scale', degree=3, random_state=42):
    """
    运行SVM分类实验
    :param test_size: 测试集比例
    :param kernel: 核函数类型
    :param C: 正则化参数
    :param gamma: 'rbf', 'poly'和'sigmoid'的核系数
    :param degree: 'poly'核函数的次数
    :param random_state: 随机种子
    :return: 评估指标字典
    """
    # 分割数据集
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=test_size, random_state=random_state, stratify=y
    )

    # 创建SVM分类器
    clf = svm.SVC(
        C=C,
        kernel=kernel,
        gamma=gamma,
        degree=degree,
        decision_function_shape='ovr',
        random_state=random_state,
        probability=True
    )

    # 训练模型
    clf.fit(X_train, y_train)

    # 预测
    y_pred = clf.predict(X_test)
    y_prob = clf.predict_proba(X_test)

    # 计算评估指标
    accuracy = accuracy_score(y_test, y_pred)
    report = classification_report(y_test, y_pred, target_names=target_names, output_dict=True)
    cm = confusion_matrix(y_test, y_pred)

    # 创建评估指标字典
    metrics = {
        'accuracy': accuracy,
        'report': report,
        'confusion_matrix': cm,
        'test_size': test_size,
        'kernel': kernel,
        'C': C,
        'gamma': gamma,
        'degree': degree,
        'model': clf,
        'y_test': y_test,
        'y_pred': y_pred,
        'y_prob': y_prob
    }

    return metrics


# 可视化混淆矩阵
def plot_confusion_matrix(cm, classes, title, filename):
    plt.figure(figsize=(8, 6))
    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
                xticklabels=classes, yticklabels=classes)
    plt.title(title)
    plt.ylabel('真实标签')
    plt.xlabel('预测标签')
    plt.tight_layout()
    plt.savefig(filename, dpi=120)
    plt.close()  # 关闭图形，避免在非交互环境中显示


# 打印分类报告
def print_classification_report(report_dict):
    """打印格式化的分类报告"""
    # 打印每个类别的指标
    print("{:<15} {:<10} {:<10} {:<10} {:<10}".format('类别', '精确率', '召回率', 'F1值', '支持数'))
    for class_name in target_names:
        metrics = report_dict[class_name]
        print("{:<15} {:<10.3f} {:<10.3f} {:<10.3f} {:<10}".format(
            class_name,
            metrics['precision'],
            metrics['recall'],
            metrics['f1-score'],
            int(metrics['support'])
        ))

    # 打印整体指标
    print("\n整体指标:")
    print("准确率: {:.3f}".format(report_dict['accuracy']))
    print("宏平均精确率: {:.3f}".format(report_dict['macro avg']['precision']))
    print("宏平均召回率: {:.3f}".format(report_dict['macro avg']['recall']))
    print("宏平均F1值: {:.3f}".format(report_dict['macro avg']['f1-score']))
    print("加权平均F1值: {:.3f}".format(report_dict['weighted avg']['f1-score']))


# 主实验函数
def main_experiment():
    results = []

    # 1. 比较不同分割比例
    print("\n" + "=" * 50)
    print("比较不同分割比例 (使用rbf核函数, C=1.0)")
    print("=" * 50)

    # 8:2分割
    print("\n>>> 8:2分割比例 <<<")
    metrics_80 = run_svm_experiment(test_size=0.2, kernel='rbf', C=1.0)
    results.append(metrics_80)

    print(f"准确率: {metrics_80['accuracy']:.4f}")
    print("分类报告:")
    print_classification_report(metrics_80['report'])

    plot_confusion_matrix(metrics_80['confusion_matrix'], target_names,
                          f"混淆矩阵 (8:2分割, 准确率={metrics_80['accuracy']:.4f})",
                          "confusion_matrix_80.png")

    # 7:3分割
    print("\n>>> 7:3分割比例 <<<")
    metrics_70 = run_svm_experiment(test_size=0.3, kernel='rbf', C=1.0)
    results.append(metrics_70)

    print(f"准确率: {metrics_70['accuracy']:.4f}")
    print("分类报告:")
    print_classification_report(metrics_70['report'])

    plot_confusion_matrix(metrics_70['confusion_matrix'], target_names,
                          f"混淆矩阵 (7:3分割, 准确率={metrics_70['accuracy']:.4f})",
                          "confusion_matrix_70.png")

    # 分割比例比较分析
    print("\n分割比例比较分析:")
    print(f"8:2分割准确率: {metrics_80['accuracy']:.4f}")
    print(f"7:3分割准确率: {metrics_70['accuracy']:.4f}")
    diff = metrics_80['accuracy'] - metrics_70['accuracy']
    print(f"差异: {diff:.4f} ({'8:2更好' if diff > 0 else '7:3更好'})")

    # 2. 调整正则化参数C
    print("\n" + "=" * 50)
    print("调整正则化参数C (使用rbf核函数, 8:2分割)")
    print("=" * 50)

    c_results = []
    for c_value in [0.1, 1.0, 10.0, 100.0]:
        print(f"\n>>> C={c_value} <<<")
        metrics_c = run_svm_experiment(test_size=0.2, kernel='rbf', C=c_value)
        results.append(metrics_c)
        c_results.append(metrics_c)

        print(f"准确率: {metrics_c['accuracy']:.4f}")
        print("分类报告:")
        print_classification_report(metrics_c['report'])

    # 3. 修改核函数
    print("\n" + "=" * 50)
    print("修改核函数 (C=1.0, 8:2分割)")
    print("=" * 50)

    kernel_results = []
    kernels = ['linear', 'poly', 'rbf', 'sigmoid']
    for kernel in kernels:
        print(f"\n>>> 核函数={kernel} <<<")
        metrics_kernel = run_svm_experiment(test_size=0.2, kernel=kernel, C=1.0)
        results.append(metrics_kernel)
        kernel_results.append(metrics_kernel)

        print(f"准确率: {metrics_kernel['accuracy']:.4f}")
        print("分类报告:")
        print_classification_report(metrics_kernel['report'])

    # 4. 调整其他参数 (gamma和degree)
    print("\n" + "=" * 50)
    print("调整gamma和degree参数 (使用poly核函数, C=1.0, 8:2分割)")
    print("=" * 50)

    # 调整gamma
    print("\n>>> 调整gamma参数 <<<")
    gammas = ['scale', 'auto', 0.1, 1.0]
    gamma_results = []
    for gamma in gammas:
        print(f"\n>>> gamma={gamma} <<<")
        metrics_gamma = run_svm_experiment(test_size=0.2, kernel='rbf', C=1.0, gamma=gamma)
        results.append(metrics_gamma)
        gamma_results.append(metrics_gamma)

        print(f"准确率: {metrics_gamma['accuracy']:.4f}")
        print("分类报告:")
        print_classification_report(metrics_gamma['report'])

    # 调整degree (只对poly核有效)
    print("\n>>> 调整degree参数 <<<")
    degree_results = []
    degrees = [2, 3, 4, 5]
    for degree in degrees:
        print(f"\n>>> degree={degree} <<<")
        metrics_degree = run_svm_experiment(test_size=0.2, kernel='poly', C=1.0, degree=degree)
        results.append(metrics_degree)
        degree_results.append(metrics_degree)

        print(f"准确率: {metrics_degree['accuracy']:.4f}")
        print("分类报告:")
        print_classification_report(metrics_degree['report'])

    # 汇总结果分析
    print("\n" + "=" * 50)
    print("实验结果汇总分析")
    print("=" * 50)

    # 分割比例比较
    print("\n分割比例比较:")
    print(f"8:2分割准确率: {metrics_80['accuracy']:.4f}")
    print(f"7:3分割准确率: {metrics_70['accuracy']:.4f}")
    diff = metrics_80['accuracy'] - metrics_70['accuracy']
    print(f"差异: {diff:.4f} ({'8:2更好' if diff > 0 else '7:3更好'})")

    # C值影响分析
    print("\n正则化参数C的影响:")
    for m in c_results:
        print(f"C={m['C']}: 准确率={m['accuracy']:.4f}")

    # 核函数比较
    print("\n核函数比较:")
    for m in kernel_results:
        print(f"核函数={m['kernel']}: 准确率={m['accuracy']:.4f}")

    # gamma影响分析
    print("\ngamma参数影响 (rbf核):")
    for m in gamma_results:
        print(f"gamma={m['gamma']}: 准确率={m['accuracy']:.4f}")

    # degree影响分析
    print("\ndegree参数影响 (poly核):")
    for m in degree_results:
        print(f"degree={m['degree']}: 准确率={m['accuracy']:.4f}")

    # 最佳参数组合
    best_result = max(results, key=lambda x: x['accuracy'])
    print("\n最佳参数组合:")
    print(f"分割比例: {1 - best_result['test_size']:.1f}:{best_result['test_size']:.1f}")
    print(f"核函数: {best_result['kernel']}")
    print(f"C值: {best_result['C']}")
    if 'gamma' in best_result:
        print(f"gamma: {best_result['gamma']}")
    if 'degree' in best_result:
        print(f"degree: {best_result['degree']}")
    print(f"准确率: {best_result['accuracy']:.4f}")

    return results


if __name__ == "__main__":
    results = main_experiment()